# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.


import logging
from odoo import http, api
from odoo.http import request
from odoo import registry as registry_get
from odoo.service import security

from odoo import SUPERUSER_ID, _

import random

try:
    from . import mail

except Exception as e:
    mail = None


from . import model2
Model2 = model2.Model2

_logger = logging.getLogger(__name__)


class JsonApi(http.Controller):
    @http.route('/api/test1', type='json', auth='none', cors='*', csrf=False)
    def test1(self, **kw):
        print('hello')
        return "hello!"

    @http.route('/api/call', type='json', auth='user', cors='*', csrf=False)
    def api_call(self, model, method, args, kwargs, sudo=None):
        return self._api_call(model, method, args, kwargs)

    @http.route('/api/login', type='json', auth='none', cors='*', csrf=False)
    def login(self, db, login, password, **kw):
        return self._login(db, login, password, **kw)

    @http.route('/api/userinfo', type='json', auth='user', cors='*', csrf=False)
    def userinfo(self):
        return self._userinfo()

    @http.route('/api/logout', type='json', auth='user', cors='*', csrf=False)
    def destroy(self):
        self._destroy()

    @http.route('/api/sms/login', type='json', auth='none', cors='*', csrf=False)
    def sms_login(self, db, mobile, code):
        return self._sms_login(db, mobile, code)

    @http.route('/api/sms/resetpsw', type='json', auth='none', cors='*', csrf=False)
    def sms_resetpsw(self, db, mobile, code, password):
        return self._sms_resetpsw(db, mobile, code, password)

    @http.route('/api/sms/register', type='json', auth='none', cors='*', csrf=False)
    def register(self, db, mobile, code, login, password):
        return self._sms_register(db, mobile, code, login, password)

    @http.route('/api/sms/send', type='json', auth='none', cors='*', csrf=False)
    def sms_send(self, db, mobile, tosend=None):
        _logger.info('call sms_send with %s, %s,%s,', db, mobile, tosend)
        return self._sms_send(db, mobile, tosend)

    @http.route('/api/sms/back', type='json', auth='none', cors='*', csrf=False)
    def sms_back(self, db, mobile):
        return self._sms_back(db, mobile)

    @http.route('/api/email/resetpsw', type='json', auth='none', cors='*', csrf=False)
    def email_resetpsw(self, db, email, code, password):
        return self._email_resetpsw(db, email, code, password)

    @http.route('/api/email/register', type='json', auth='none', cors='*', csrf=False)
    def email_register(self, db, email, code, password):
        return self._email_register(db, email, code, password)

    @http.route('/api/email/send', type='json', auth='none', cors='*', csrf=False)
    def email_send(self, db, email, tosend=None):
        _logger.info('call email_send with %s, %s,%s,', db, email, tosend)
        return self._email_send(db, email, tosend)

    @http.route('/api/email/back', type='json', auth='none', cors='*', csrf=False)
    def email_back(self, db, email):
        return self._email_back(db, email)

    def _api_call(self, model, method, args, kwargs):
        _logger.info('model %s call %s with %s, %s',
                     model, method, args, kwargs)
        odoo_model = request.env[model]

        # print('context,env.context,', odoo_model.env.context)
        # print('context,_context', odoo_model._context)

        context = kwargs.get('context') or {}
        context.update(odoo_model._context)
        odoo_model = odoo_model.with_context(context)

        print('context,env.context 2,', odoo_model.env.context)
        # print('context,_context 2', odoo_model._context)

        # fields_get2, return multi model
        # search_count2 and search_read2, domain is dict
        # search_read2, read2, fields is dict
        # 2019-11-27 TBD,   getOption only for m2o?

        if method in [
            'test_function',
            'export_base64',
            'default_get2',  # fields 重构
            'onchange2',  # 自动补充参数
            'fields_get2',  # do nothing
            'search_count2',  # domain 重构
            'search_read2',  # domain 重构, fields 重构 递归
            'read2',  # fields 重构 递归
            'search_read1', 'read1',  # domain, fields 重构 不递归
            'create2', 'write2',  # fields 重构 递归
            'create1', 'write1',  # fields 重构 不递归
            # 'create', 'write',  #  onchange 的处理 改方法了.
            'get_options',
            'report_print',  # account 报表

            # 'wizard_create', # 直接用 default get 就可以了
            'wizard_submit',
        ]:

            ret = {
                'test_function':   Model2.test_function,
                'export_base64': Model2.export_base64,
                'default_get2':   Model2.default_get,
                'onchange2':   Model2.onchange,
                'fields_get2':   Model2.fields_get,
                # 'create':        Model2.create,
                # 'write':         Model2.write,
                'search_count2': Model2.search_count2,
                'search_read1':  Model2.search_read1,
                'search_read2':  Model2.search_read2,
                'read1':         Model2.read1,
                'read2':         Model2.read2,
                'create1':       Model2.create1,
                'create2':       Model2.create2,
                'write1':        Model2.write1,
                'write2':        Model2.write2,

                'get_options':   Model2.get_options,
                # 'wizard_create':   Model2.wizard_create,
                'wizard_submit':   Model2.wizard_submit,
                'report_print':    Model2.report_print,

            }[method](odoo_model, *args, **kwargs)

            return ret

        return api.call_kw(odoo_model, method, args, kwargs)

    def _login(self, db, login, password, **kw):
        _logger.info('call login with %s, %s, %s', db, login, password)
        request.session.authenticate(db, login, password)
        request.session.rotate = False

        return Auth.get_sessioninfo()

    def _userinfo(self,  **kw):
        _logger.info('call _userinfo ')
        return Auth.get_sessioninfo()

    def _destroy(self):
        # 安装销售模块后, utm 模块 会找 db
        request.session.logout(keep_db=True)

    def _email_send(self, db, email, tosend):
        _logger.info('call email with %s, %s', db, email)
        # 使用验证码 重置密码时, 先检查 email 是否存在, 不存在则不发
        # 使用验证码注册时, 必发
        # 若电话号码已存在, 则? TBD 2020-4-27

        domain = [('email', '=', email)]
        registry = registry_get(db)
        context = {}
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            user = User.search(domain)
            print('_email_send,', user, user.id, user.login, user.name)

            if not (user or tosend):
                return None

        code = str(int((random.random() + 1) * 10000))[1:]

        request.session.auth_login = {
            'db': db,
            'email': email,
            'code': code
        }

        print('request.session._email_send,', request.session.auth_login)

        # then call email api to send msg
        if mail:
            # 在 user model 中定义一个 函数, 取邮件的格式
            print('send mail, user,', user, hasattr(user, 'format_mail'))
            if hasattr(user, 'format_mail'):
                code2 = user.format_mail(code)
            else:
                code2 = code

            mm = mail.Mail()
            mm.send(email, code2)

        return True

    def _email_back(self, db, email):
        auth_login = request.session.auth_login or {}
        # print('request.session._email_back, server', request.session.auth_login)
        # print('request.session._email_back, client', db, email)
        if isinstance(auth_login, dict) and auth_login.get('db') == db \
                and auth_login.get('email') == email:
            # print('request.session._email_back, ok', auth_login.get('code') )

            return auth_login.get('code')
        return None

    def _check_email_code(self, db, email, code):
        auth_login = request.session.auth_login
        if not auth_login:
            return None
        if not isinstance(auth_login, dict):
            return None
        if auth_login.get('email') != email:
            return None
        if auth_login.get('code') != code:
            return None
        return True

    def _email_register(self, db, email, code, password):
        _logger.info('call _email_register with %s, %s', db, email)
        if not self._check_email_code(db, email, code):
            return None

        ret = Auth.email_register(db, email, password)

        return ret

    def _email_resetpsw(self, db, email, code, password):
        _logger.info('call _email_resetpsw with %s, %s', db, email)
        if not self._check_email_code(db, email, code):
            return None

        domain = [('email', '=', email)]
        ret = Auth.reset_password(db, domain, password)

        _logger.info('call _email_resetpsw return:  %s', ret)

        return ret

    def _sms_send(self, db, mobile, tosend):
        _logger.info('call sms with %s, %s', db, mobile)
        # 使用验证码登录时, 先检查 电话号码是否存在, 不存在则不发
        # 使用验证码注册时, 若电话号码已存在, 则? TBD 2020-4-27

        domain = [('mobile', '=', mobile)]
        registry = registry_get(db)
        context = {}
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            user = User.search(domain)
            print('_sms_send,', user, user.id, user.login, user.name)

            if not (user or tosend):
                return None

        request.session.auth_login = {
            'db': db,
            'mobile': mobile,
            'code': str(int((random.random() + 1) * 10000))[1:]
        }

        print('request.session._sms_send,', request.session.auth_login)

        # then call sms api to send msg

        return True

    def _sms_back(self, db, mobile):
        auth_login = request.session.auth_login or {}
        # print('request.session._sms_back, server', request.session.auth_login)
        # print('request.session._sms_back, client', db, mobile)
        if isinstance(auth_login, dict) and auth_login.get('db') == db \
                and auth_login.get('mobile') == mobile:
            # print('request.session._sms_back, ok', auth_login.get('code') )

            return auth_login.get('code')
        return None

    def _check_sms_code(self, db, mobile, code):
        auth_login = request.session.auth_login
        if not auth_login:
            return None
        if not isinstance(auth_login, dict):
            return None
        if auth_login.get('mobile') != mobile:
            return None
        if auth_login.get('code') != code:
            return None
        return True

    def _sms_register(self, db, mobile, code, login, password):
        _logger.info('call _sms_register with %s, %s', db, mobile)
        if not self._check_sms_code(db, mobile, code):
            return None

        ret = Auth.register(db, mobile, login, password)

        return ret

    def _sms_resetpsw(self, db, mobile, code, password):
        _logger.info('call _sms_resetpsw with %s, %s', db, mobile)
        if not self._check_sms_code(db, mobile, code):
            return None

        domain = [('mobile', '=', mobile)]
        ret = Auth.reset_password(db, domain, password)

        _logger.info('call _sms_resetpsw return:  %s', ret)

        return ret

    def _sms_login(self, db, mobile, code):
        _logger.info('call _sms_login with %s, %s', db, mobile)
        if not self._check_sms_code(db, mobile, code):
            return None

        domain = [('mobile', '=', mobile)]

        uid = Auth.authenticate(db, domain)
        if uid:
            return Auth.get_sessioninfo()

        return None


class Auth(object):
    @classmethod
    def get_sessioninfo(cls):
        session_info = request.env['ir.http'].session_info()
        # for odoo 13, session_id is not in session_info
        if not session_info.get('session_id'):
            session_info['session_id'] = request.session.sid

        csrf_token = request.csrf_token()
        session_info['csrf_token'] = csrf_token
        print('xxxxxx, session_info,', session_info)

        return session_info

    @classmethod
    def register(cls, db, mobile, login, password):
        registry = registry_get(db)
        context = {}
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            ret = User.create({
                'mobile': mobile,
                'email': login,
                'name': login,
                'login': login,
                'password': password
            })

            return ret

    @classmethod
    def email_register(cls, db, email, password):
        registry = registry_get(db)
        context = {}
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            ret = User.create({
                'email': email,
                'name': email,
                'login': email,
                'password': password
            })

            return ret

    @classmethod
    def reset_password(cls, db, domain, password):
        registry = registry_get(db)
        context = {}
        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            user = User.search(domain)
            ret = user.write({
                'password': password
            })

            return ret

    @classmethod
    def authenticate(cls, db, domain):
        registry = registry_get(db)
        context = {}

        with registry.cursor() as cr:
            env = api.Environment(cr, SUPERUSER_ID, context)
            User = env['res.users']
            user = User.search(domain)
            if not user:
                return None

            user = user.with_user(user)
            user._update_last_login()

            return _authenticate(db, user.login, user.id)


def _authenticate(db, login, uid):
    self = request.session
    self.rotate = False
    self.db = db
    self.uid = uid
    self.login = login
    self.session_token = uid and security.compute_session_token(
        self, request.env)
    request.uid = uid
    request.disable_db = False

    if uid:
        self.get_context()
    return uid
